package cn.kgc.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.kgc.entity.Resume;
import cn.kgc.entity.TccRecord;
import cn.kgc.mapper.ResumeMapper;
import cn.kgc.service.ResumeTccService;
import cn.kgc.service.TccRecordService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author wangyue
 * @version 1.0
 */
@Service("resumeTccService")
@Slf4j
@SuppressWarnings("all")
public class ResumeTccServiceImpl  implements ResumeTccService {
    @Resource
    private TccRecordService tccRecordService;
    @Resource
    private ResumeMapper resumeMapper;


    @Override
    @Transactional
    public boolean tryInitResume(BusinessActionContext actionContext, String userId) {
        //获得全局事务id
        String xid = actionContext.getXid();
        //获得分支事务id
        String branchId = actionContext.getBranchId()+"";
        if(StrUtil.isBlank(xid)|| StrUtil.isBlank(branchId)){
            log.error("全局事务id或者分支事务id不能为空");
            return false;
        }
        //1 判断是否已经执行过try操作,如果已经执行过try操作,不能在执行try操作。为了实现try操作的幂等性
        if(tccRecordService.hasTry(xid,branchId)){
            log.warn("全局事务{}下的分支事务{}中的try操作已经执行过,不需要重复执行",xid,branchId);
            return false;
        }
        //2 判断是否已经执行过cancel操作,如果已经执行过cancel操作,不能在执行try操作。为了防止业务悬挂。
        if(tccRecordService.hasCancel(xid,branchId)){
            log.warn("全局事务{}下的分支事务{}中的cancel操作已经执行过,不能执行try操作",xid,branchId);
            return false;
        }
        Resume resume = Resume.builder().userId(userId).build();
        resumeMapper.insert(resume);
        //向TccRecord表中插入一条记录,表示当前分支事务中的try操作已经执行过了
        TccRecord tccRecord = TccRecord.builder()
                //执行的try操作
                .status((short) 1)
                //全局事务id
                .xid(xid)
                //分支事务id
                .branchId(branchId)
                //业务方法名
                .actionName("tryInitResume")
                //业务方法参数要求json格式的参数
                .args(
                        JSONUtil.toJsonStr(
                            new String[]{userId}
                        )
                ).build();
        tccRecordService.save(tccRecord);
        return true;
    }

    @Override
    public boolean commitInitResume(BusinessActionContext actionContext) {
        //1 如果全局事务下的所有分支事务的try操作都没有问题,执行分支事务中的所有commit方法。当然也包含
        //当前分支事务中的commit方法（commitInitResume）

        //2 commit操作可以为空,但是返回值必须是true。否则,TC会不断的重复调用我们的commitInitResume方法。

        //3 如果commit方法不为空,则需要执行以下操作:
        //(1) 判断是否已经执行过commit方法,如果已经执行过commit方法则不需要再重复执行commit方法。
        //(2) 直线完commit操作以后,千万要记得向tcc_record表中插入一条记录,说明当前全局事务下的分支事务的commit
        //操作已经执行过了。
        return true;
    }


    @Override
    @Transactional
    public boolean rollbackInitResume(BusinessActionContext actionContext) {
        //只要全局事务下的所有分支事务的try操作有一个有问题,执行分支事务中的所有cancel方法。
       String xid=actionContext.getXid();
       String branchId = actionContext.getBranchId()+"";
       String userId=(String) actionContext.getActionContext("userId");
       //1 判断全局事务中的分支事务的cancel操作是否执行过,如果执行过则不需要重复执行。 保证幂等性。
       if(tccRecordService.hasCancel(xid,branchId)){
           log.warn("全局事务{}下的分支事务{}中的cancel操作已经执行过,不需要重复执行",xid,branchId);
           return true;
       }
       //2 如果全局事务中的分支事务中的try操作还没有执行过,则不能先执行分支事务中的cancel操作。防止空回滚。
       if(!tccRecordService.hasTry(xid,branchId)){
           log.warn("全局事务{}下的分支事务{}中的try操作还没有执行过,不能先执行cancel操作",xid,branchId);
           TccRecord tccRecord = TccRecord.builder()
                   .status((short) 3)  //执行的cancel操作
                   .xid(xid) //全局事务id
                   .branchId(branchId)  //分支事务id
                   .actionName("rollbackInitResume")
                   .args(
                           JSONUtil.toJsonStr(new String[]{userId})
                   ).build();
           return tccRecordService.save(tccRecord);
       }
       //3 执行分支事务中的cancel操作
       resumeMapper.delete(
               new LambdaQueryWrapper<Resume>().eq(Resume::getUserId,userId)
       );
       //不要忘记更新TCC_RECORD中的当前事务的中状态。状态从try状态改为cancel状态。
        LambdaUpdateWrapper<TccRecord> updateWrapper=new LambdaUpdateWrapper<>();
        updateWrapper.eq(TccRecord::getXid,xid);
        updateWrapper.eq(TccRecord::getBranchId,branchId);
        updateWrapper.set(TccRecord::getStatus,(short)3);
        return tccRecordService.update(updateWrapper);
    }
}